package com.dreamlin.basepro.exceptions;


import androidx.core.util.Supplier;

import org.reactivestreams.Publisher;

import java.util.concurrent.TimeUnit;

import io.reactivex.Flowable;
import io.reactivex.Observable;
import io.reactivex.ObservableSource;
import io.reactivex.Single;
import io.reactivex.functions.Function;


public class RetryConfig {

    int maxRetries = 1;
    long delay = 1000L;
    Supplier<Single<Boolean>> retryCondition = () -> Single.just(false);

    public RetryConfig() {
    }

    public RetryConfig(int maxRetries, long delay, Supplier<Single<Boolean>> retryCondition) {
        this.maxRetries = maxRetries;
        this.delay = delay;
        this.retryCondition = retryCondition;
    }
}

class FlowRetryDelay implements Function<Flowable<Throwable>, Publisher<?>> {
    private Function<Throwable, RetryConfig> provider;
    private int retryCount = 0;

    public FlowRetryDelay(Function<Throwable, RetryConfig> provider) {
        this.provider = provider;
    }

    @Override
    public Publisher<?> apply(Flowable<Throwable> throwableFlowable) {
        return throwableFlowable
                .flatMap(throwable -> {
                    RetryConfig retryConfig = provider.apply(throwable);
                    long delay = retryConfig.delay;
                    if (++retryCount <= retryConfig.maxRetries) {
                        retryConfig.retryCondition
                                .get()
                                .flatMapPublisher(retry -> {
                                    if (retry)
                                        return Flowable.timer(delay, TimeUnit.MILLISECONDS);
                                    else
                                        return Flowable.error(throwable);
                                });
                    }
                    return Flowable.error(throwable);
                });
    }
}

class ObservableRetryDelay implements Function<Observable<Throwable>, ObservableSource<?>> {
    private Function<Throwable, RetryConfig> provider;
    private int retryCount = 0;

    public ObservableRetryDelay(Function<Throwable, RetryConfig> provider) {
        this.provider = provider;
    }

    @Override
    public ObservableSource<?> apply(Observable<Throwable> throwableObservable) {
        return throwableObservable.flatMap(throwable -> {
            RetryConfig retryConfig = provider.apply(throwable);
            long delay = retryConfig.delay;
            if (++retryCount <= retryConfig.maxRetries) {
                retryConfig.retryCondition
                        .get()
                        .flatMapObservable(retry -> {
                            if (retry)
                                return Observable.timer(delay, TimeUnit.MILLISECONDS);
                            else
                                return Observable.error(throwable);
                        });
            }
            return Observable.error(throwable);
        });
    }
}
